/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.driver.nodehandler.instancea;

import com.jphenix.driver.nodehandler.exception.NodeHandlerException;
import com.jphenix.driver.nodehandler.util.NodeHandlerUtil;
import com.jphenix.share.tools.Base64;
import com.jphenix.share.tools.MD5;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * HTML信息解析类
 * 
 * 注意：script标签，style标签中的内容是不做任何解析的
 * 
 * 2018-07-26 修改了获取节点属性容器对象方法，避免返回的属性容器对象中的值被移除，使xml中原始属性值也丢失
 * 
 * @author 刘虻
 * 2009-10-21上午11:25:00
 */
@ClassInfo({"2018-07-26 12:45","XML HTML信息解析类"})
public class NodeHandler extends Node implements INodeHandler {

	private   String md5Value = null;                      //文件MD5值
	protected final int DEFAULT_READ_BUFFER_SIZE = 1024;  //默认读取文件内容缓存大小
	protected String filePath = null;                      //html文件路径 
	protected ArrayList<String> noDealChildTagList = null; //不需要处理的标签
	protected long ts = 0;                                 //时间戳，用来记录文件修改时间
	
	/**
	 * 构造函数
	 * 2009-10-21上午11:25:02
	 */
	public NodeHandler() {
		super();
	}
	
	
	/**
	 * 获取不处理其标签内容作为操作标签的标签容器
	 * @author 刘虻
	 * 2009-10-21下午01:52:43
	 * @return 不处理其标签内容作为操作标签的标签容器
	 */
	protected ArrayList<String> getNoDealChildTagList() {
		if (noDealChildTagList==null) {
			noDealChildTagList = new ArrayList<String>();
			noDealChildTagList.add("script");
			noDealChildTagList.add("style");
			noDealChildTagList.add("no_active"); //如果不想解析某块内容，则在外围扩上这个标签名
		}
		return noDealChildTagList;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-21上午11:25:00
	 */
	@Override
    public String getFilePath() {
		if(filePath==null) {
			filePath = "";
		}
		return filePath;
	}


	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-21上午11:25:00
	 */
	@Override
    public INodeHandler setFilePath(String filePath)
			throws Exception {
		this.filePath = filePath;
		this.locationInfo = filePath;
		outSelf = false; //根节点默认不显示
		parse(getNodeBodyFromFile(filePath),0);//解析html
		isEmpty = false; //设置非空节点
		return this;
	}
	
	
	/**
	 * 设置HTML文件对象
	 * 刘虻
	 * 2010-6-8 下午06:13:02
	 * @param htmlFile HTML文件对象
	 * @return HTML对象
	 * @throws Exception 执行发生异常
	 */
	@Override
    public INodeHandler setFile(
			File htmlFile) throws Exception {
		if(htmlFile==null) {
			throw new NodeHandlerException("The HtmlFile Is Null");
		}
		this.filePath = htmlFile.getPath();
		this.locationInfo = this.filePath;
		outSelf = false; //根节点默认不显示
		parse(getNodeBodyFromFile(htmlFile),0);//解析html
		isEmpty = false; //设置非空节点
		return this;
	}
	
	
	/**
	 * 设置文件读入流
	 * @param is          文件读入流
	 * @param filePath    文件路径（用作输出日志等表示信息，无实际用处）
	 * @return 当前类实例
	 * @throws Exception  异常
	 * 2017年2月22日
	 * @author MBG
	 */
	public INodeHandler setInputStream(InputStream is,String filePath) throws Exception {
		if(is==null) {
			throw new NodeHandlerException("The InputStream Is Null");
		}
		this.filePath = filePath;
		this.locationInfo = this.filePath;
		outSelf = false; //根节点默认不显示
		parse(getNodeBodyFromStream(is),0);//解析html
		isEmpty = false; //设置非空节点
		return this;
	}
	
	/**
	 * 设置HTML内容
	 * @param bytes				字节数组
	 * @return						当前类实例
	 * @throws Exception		异常
	 * 2014-3-14
	 * @author 马宝刚
	 */
	@Override
    public INodeHandler setBytes(byte[] bytes) throws Exception {
		if(bytes==null) {
			throw new NodeHandlerException("The bytes Is Null");
		}
		outSelf = false; //根节点默认不显示
		md5Value = MD5.md5(bytes); //获取字节数组（文件）的MD5值（大写）
		parse(bytes,0);
		isEmpty = false; //设置非空节点
		return this;
	}
	
	/**
	 * 设置读入流
	 * @param is 读入流
	 * @param filePath 路径信息（用来记录来源，输出到日志用）
	 * @return			当前类实例
	 * @throws Exception 异常
	 * 2014-3-14
	 * @author 马宝刚
	 */
	@Override
    public INodeHandler setStream(InputStream is, String filePath) throws Exception {
		if(is==null) {
			throw new NodeHandlerException("The Stream Is Null");
		}
		outSelf = false; //根节点默认不显示
		parse(getNodeBodyFromStream(is),0);
		this.filePath = filePath;
		this.locationInfo = filePath;
		isEmpty = false; //设置非空节点
		return this;
	}
	
	
	/**
	 * 执行解析HTML内容
	 * @author 刘虻
	 * 2009-10-23下午01:08:44
	 */
	protected int parse(byte[] nodeBody,int bodyIndex) {
		if(nodeBody==null) {
			return bodyIndex;
		}
		while(bodyIndex<nodeBody.length) {
			//构建子类
			NodeHandler hh = (NodeHandler)newInstance(true);
			bodyIndex = hh.doParse(nodeBody,bodyIndex); //处理子标签
			if(hh.isLoop) {
				continue;
			}else if(hh.isEndTag) {
				return bodyIndex;
			}else if(hh.isParameterNode) {
				getParameterMap().putAll(hh.attrMap());
			}else if(hh.isTextNode() || hh.isSingleTag() || hh.isOver) {
				hh.setParentNode(this);
				hh.isEmpty = false;
				addChildNode(hh); //放入可操作节点序列
			}else {
				hh.setParentNode(this);
				hh.isEmpty = false;
				addChildNode(hh); //放入可操作节点序列
				bodyIndex = hh.parse(nodeBody,bodyIndex);
			}
		}
		return bodyIndex;
	}
	
	
	
	
	/**
	 * 处理标签体
	 * 
	 * 循环每一个字符，解析出标签中的属性，参数，标签名信息
	 * 
	 * 支持过滤空格，tab，回车
	 * 支持转义字符，属性值中的>符号
	 * 
	 * <!--@ 内部注释节点 ,不输出 -->
	 * 
	 * 页面参数节点:
	 * <!--*   key1="value1" key2="value2" -->
	 * @author 刘虻
	 * 2009-10-20下午03:42:52
	 * @param nodeBody html体
	 * @param bodyPoint 指针
	 * @return 0标签头  1余下的标签内容
	 */
	protected int doParse(byte[] nodeBody,int bodyPoint) {
		attributeMap = new HashMap<String,String>();
		attributeSignMap = new HashMap<String,String>();
		propertyList = new ArrayList<String>();
		attributeKeyList = new ArrayList<String>();
		
		/*
		 * 标签设置过程状态
		 * 	
		 * 	 0  没设置标签名 
		 *   1  设置标签名中
		 *   2  标签名设置完毕
		 *   3 CDATA类型
		 *   4  注释
		 *   7 不需要处理的标签
		 *   8 DOCTYPE
		 */
		int nodeState = 0;
		ByteArrayOutputStream nodeNameBos = new ByteArrayOutputStream(); //标签名缓存
		byte[] endNodeBytes = null; //不处理的节点结束标识字节数组
		ByteArrayOutputStream nodeTextBos = new ByteArrayOutputStream(); //文本标签值缓存
		ByteArrayOutputStream bos1 = new  ByteArrayOutputStream();//临时存储变量，用来保存属性名，或参数
		ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); //临时存储变量，用来保存属性值
		Byte attribSign = null; //属性值分隔符
		boolean isSub = false; //是否经过了分隔符
		boolean isSpSign = false; //是否为特殊字符
		boolean isSwapSign = false; //是否为转义字符
		boolean isAttribValue = false; //是否为属性值
		boolean isSingleEndSign = false; //是否为无值标签结束字符
		int thisIndex = -1; //当前循环索引 
		
		//转换为字符数组
		for(int i=bodyPoint;i<nodeBody.length;i++) {
			bodyPoint++;
			thisIndex++;
			if(nodeState==0) {
				if(nodeBody[i]=='<') {
					if(isTextNode) {
						//到下一个节点之前，如果处理的都是纯文本，则生成纯文本节点
						nodeBodyText = valueToString(nodeTextBos.toByteArray());
						return bodyPoint-1; //退回到<前面,使其指向<
					}
					nodeState = 1;
				}else {
					if(thisIndex==0) {
						//第一次循环
						isTextNode = true;
					}
					nodeTextBos.write(nodeBody[i]);
				}
			}else {
				if(thisIndex==1 && nodeBody[i]=='/') {
					//为标签结束符
					isEndTag = true;
				}else if(isEndTag) {
					if(nodeBody[i]==' ' || nodeBody[i]=='	' || nodeBody[i]=='\r' || nodeBody[i]=='\n') {
						continue;
					}else if(nodeBody[i]=='>') {
						break;
					}
					nodeNameBos.write(nodeBody[i]);
				}else if(nodeState==1 || nodeState==2){
					if(nodeState==1) {
						if(nodeBody[i]==' ' || nodeBody[i]=='	' || nodeBody[i]=='\r' || nodeBody[i]=='\n') {
							nodeState = 2;
							//设置节点名
							nodeName = valueToString(nodeNameBos.toByteArray());
						}else if(nodeBody[i]=='/') {
							isSingleEndSign = true;
						}else if(nodeBody[i]=='>') {
							//设置节点名
							nodeName = valueToString(nodeNameBos.toByteArray());
							if(isSingleEndSign) {
								isSpSign = true;
							}else if(getNoDealChildTagList().contains(nodeName)) {
								nodeState = 7;
								//节点名字节数组
								byte[] nodeBytes = nodeNameBos.toByteArray();
								endNodeBytes = new byte[nodeBytes.length+3];
								endNodeBytes[0] = '<';
								endNodeBytes[1] = '/';
								for(int j=0;j<nodeBytes.length;j++) {
									endNodeBytes[j+2] = nodeBytes[j];
								}
								endNodeBytes[endNodeBytes.length-1] = '>';
								continue;
							}
							break;
						} else {
							nodeNameBos.write(nodeBody[i]);
							if(nodeBody[i]=='!') {
								if(nodeBody.length>i+3 && nodeBody[i+1]=='-' && nodeBody[i+2]=='-' && nodeBody[i+3]=='@') {
									//内部注释（不需要输出的）
									nodeTextBos.write('<');
									nodeTextBos.write('!');
									nodeTextBos.write('-');
									nodeTextBos.write('-');
									nodeTextBos.write('@');
									nodeState = 4;
									bodyPoint+=3;
									i+=3;
									outSelf = false;
									outAll = false;
									continue;
								}else if(nodeBody.length>i+3 && nodeBody[i+1]=='-' && nodeBody[i+2]=='-' && nodeBody[i+3]=='*') {
									//导入参数节点
									isParameterNode = true;
									isSingleTag = true;
								}else if(nodeBody.length>i+2 && nodeBody[i+1]=='-' && nodeBody[i+2]=='-') {
									//外部注释（需要输出的）
									nodeTextBos.write('<');
									nodeTextBos.write('!');
									nodeTextBos.write('-');
									nodeTextBos.write('-');
									nodeState = 4;
									bodyPoint+=2;
									i+=2;
									continue;
								}else if(nodeBody.length>i+7 
										&& nodeBody[i+1]=='[' 
											&& (nodeBody[i+2]=='C' || nodeBody[i+2]=='c')
											&& (nodeBody[i+3]=='D' || nodeBody[i+2]=='d')
											&& (nodeBody[i+4]=='A' || nodeBody[i+2]=='a')
											&& (nodeBody[i+5]=='T' || nodeBody[i+2]=='t')
											&& (nodeBody[i+6]=='A' || nodeBody[i+2]=='a')
											&& nodeBody[i+7]=='[') {
									bodyPoint+=7;
									i+=7;
									nodeState = 3;
									continue;
								}else if(nodeBody.length>i+7 
										&& (nodeBody[i+1]=='D' || nodeBody[i+1]=='d')
										&& (nodeBody[i+2]=='O' || nodeBody[i+2]=='o')
										&& (nodeBody[i+3]=='C' || nodeBody[i+2]=='c')
										&& (nodeBody[i+4]=='T' || nodeBody[i+2]=='t')
										&& (nodeBody[i+5]=='Y' || nodeBody[i+2]=='y')
										&& (nodeBody[i+6]=='P' || nodeBody[i+2]=='p')
										&& (nodeBody[i+7]=='E' || nodeBody[i+7]=='e')) {
									bodyPoint+=7;
									i+=7;
									nodeState = 8;
									nodeTextBos.write('<');
									nodeTextBos.write('!');
									nodeTextBos.write('D');
									nodeTextBos.write('O');
									nodeTextBos.write('C');
									nodeTextBos.write('T');
									nodeTextBos.write('Y');
									nodeTextBos.write('P');
									nodeTextBos.write('E');
									continue;
								}
							}else if(thisIndex==1 && nodeBody[i]=='?') {
								nodeTextBos.write('<');
								nodeTextBos.write('?');
								nodeState = 5;
								continue;
							}
						}
					}else {
						if(isAttribValue) {
							if(nodeBody[i]=='\\') {
								//标记为转义字符符号
								isSpSign = !isSpSign;
								continue;
							}
							if(isSpSign) {
								//当前字符是转义
								isSpSign = false;
								isSwapSign = true;
							}
							if(attribSign==null) {
								if((nodeBody[i]==' ' 
										|| nodeBody[i]=='	' 
										|| nodeBody[i]=='\r' 
										|| nodeBody[i]=='\n') && !isSwapSign) {
									continue;
								}else if((nodeBody[i]=='\'' || nodeBody[i]=='\"') && !isSwapSign) {
									attribSign = new Byte(nodeBody[i]);
								}else {
									if(isSwapSign) {
										bos2.write('\\');
										isSwapSign = false;
									}
									bos2.write(nodeBody[i]);
									attribSign = new Byte((byte)'0');
								}
							}else {
								if(isSwapSign) {
									bos2.write('\\');
									bos2.write(nodeBody[i]);
									isSwapSign = false;
								}else if((attribSign.byteValue()=='0'
										&& (nodeBody[i]==' ' 
												|| nodeBody[i]=='	' 
												|| nodeBody[i]=='\r' 
												|| nodeBody[i]=='\n')) 
									|| nodeBody[i]==attribSign.byteValue()) {
									isAttribValue = false;
									//属性主键
									String key = valueToString(bos1.toByteArray()).toLowerCase();
									if(!attributeKeyList.contains(key)) {
										attributeKeyList.add(key);
									}
									attributeMap.put(key,valueToString(bos2.toByteArray()));
									byte signByte = attribSign.byteValue(); //属性分割符
									attributeSignMap.put(key,signByte=='0'?"":String.valueOf((char)signByte));
									bos1 = new ByteArrayOutputStream();
									bos2 = new ByteArrayOutputStream();
									attribSign = null;
								}else {
									bos2.write(nodeBody[i]);
								}
							}
						}else {
							if(isSwapSign) {
								bos1.write('\\');
								bos1.write(nodeBody[i]);
								isSwapSign = false;
							}else if(nodeBody[i]==' ' || nodeBody[i]=='	' || nodeBody[i]=='\r' || nodeBody[i]=='\n') {
								isSub = true;
								continue;
							}else {
								if(nodeBody[i]=='=') {
									//准备设置属性值
									isAttribValue = true;
								}else if(nodeBody[i]=='/') {
									isSingleEndSign = true;
								}else if(nodeBody[i]=='>') {
									if(bos1.size()>0) {
										//没有属性值，将作为参数
										propertyList.add(valueToString(bos1.toByteArray()));
										bos1 = new ByteArrayOutputStream();
									}
									if(isSingleEndSign) {
										isSingleTag = true; //标记为无内容标签
									}else if(getNoDealChildTagList().contains(nodeName)) {
										nodeState = 7;
										//节点名字节数组
										byte[] nodeBytes = nodeNameBos.toByteArray();
										endNodeBytes = new byte[nodeBytes.length+3];
										endNodeBytes[0] = '<';
										endNodeBytes[1] = '/';
										for(int j=0;j<nodeBytes.length;j++) {
											endNodeBytes[j+2] = nodeBytes[j];
										}
										endNodeBytes[endNodeBytes.length-1] = '>';
										continue;
									}
									break;
								}else {
									if(isSub) {
										isSub = false;
										if(bos1.size()>0) {
											//放入参数
											propertyList.add(valueToString(bos1.toByteArray()));
											bos1 = new ByteArrayOutputStream();
										}
									}
									bos1.write(nodeBody[i]);
								}
							}
						}
					}
				}else if(nodeState==3) {
					if(nodeBody[i]==']') {
						if(nodeBody.length<i+3) {
							//节点出现错误，没有]]>结束符
							isTextNode = true;
							nodeTextBos.write(nodeBody[i]);
						}else if(nodeBody[i+1]==']' && nodeBody[i+2]=='>'){
							//标识结束 ]]>
							bodyPoint+=2;
							isTextNode = true;
							isCdata = true;
							nodeBodyText = valueToString(nodeTextBos.toByteArray());
							break;
						}
					}
					nodeTextBos.write(nodeBody[i]);
				}else if(nodeState==4) {
					if(nodeBody[i]=='-') {
						if(nodeBody.length<i+3) {
							//节点出现错误，没有-->结束符
							isTextNode = true;
							nodeTextBos.write(nodeBody[i]);
						}else if(nodeBody[i+1]=='-' && nodeBody[i+2]=='>'){
							//标识结束 -->
							bodyPoint+=2;
							isTextNode = true;
							nodeTextBos.write('-');
							nodeTextBos.write('-');
							nodeTextBos.write('>');
							nodeBodyText = valueToString(nodeTextBos.toByteArray());
							break;
						}
					}
					nodeTextBos.write(nodeBody[i]);
				}else if(nodeState==5) {
					if(nodeBody[i]=='?') {
						if(nodeBody.length<i+2) {
							//节点出现错误，没有?>结束符
							isTextNode = true;
							nodeTextBos.write(nodeBody[i]);
						}else if(nodeBody[i+1]=='>'){
							//标识结束?>
							bodyPoint+=1;
							isTextNode = true;
							nodeTextBos.write('?');
							nodeTextBos.write('>');
							nodeBodyText = valueToString(nodeTextBos.toByteArray());
							break;
						}
					}
					nodeTextBos.write(nodeBody[i]);
				}else if(nodeState==6) {
					//准备处理 不需要处理的标签 的标签内容 (读完节点内容）
					nodeTextBos.write(nodeBody[i]);
					if(nodeBody[i]=='>') {
						nodeState = 7;
					}
				}else if(nodeState==7) {
					//处理不需要处理的标签内容  (读取节点之间的内容)
					//将节点之间的内容变成一个子节点
					boolean isEnd = true; //是否标签内容结束
					if(nodeBody.length>=(i+endNodeBytes.length)) {
						for(int j=0;j<endNodeBytes.length;j++) {
							if(!(nodeBody[i+j]==endNodeBytes[j])) {
								isEnd = false;
								break;
							}
						}
						if(isEnd) {
							nodeBodyText = valueToString(nodeTextBos.toByteArray());
							setNodeText(nodeBodyText);
							bodyPoint+=endNodeBytes.length-1;
							isOver = true;
							break;
						}
					}else {
						//异常情况，剩下的内容长度小于结束标识长度
						//剩余字节
						int lastByteLength = nodeBody.length-i;
						for(int j=0;j<lastByteLength;j++) {
							nodeTextBos.write(nodeBody[i+j]);
						}
						nodeName = "";
						isTextNode = true;
						break;
					}
					nodeTextBos.write(nodeBody[i]);
				}else if(nodeState==8) {
					if(nodeBody[i]=='>') {
						isTextNode = true;
						nodeTextBos.write(nodeBody[i]);
						nodeBodyText = valueToString(nodeTextBos.toByteArray());
						break;
					}
					nodeTextBos.write(nodeBody[i]);
				}
			}
		}
		if(bodyPoint==nodeBody.length) {
			//在循环结束后，如果之前一直在记录文本节点
			if(isTextNode) {
				nodeBodyText = valueToString(nodeTextBos.toByteArray());
				return bodyPoint; //退回到<前面,使其指向<
			}
		}
		if(nodeName!=null) {
			if(!isXmlStyle() && getSingleTagNameList().contains(nodeName.toLowerCase())) {
				if(isEndTag) {
					//如果这是一个无内容节点，但采用有内容节点的写法，比如 <input > 写成了 <input></input>
					getParentNode().setSingleTag(false);
					isLoop = true;
				}
				isSingleTag = true;
			}
			outSelf = true;
		}
		return bodyPoint;
	}
	
	
	/**
	 * 将处理后的字节数组转换为字符串
	 * @author 刘虻
	 * 2009-10-22上午08:51:04
	 * @param values 字节数组
	 * @return 字符串
	 */
	protected String valueToString(byte[] values) {
		try {
			return new String(values,getDealEncode());
		}catch(Exception e) {
			e.printStackTrace();
		}
		return "";
	}
	
	
	
	/**
	 * 从文件中读取内容字节数组
	 * 刘虻
	 * 2010-6-8 下午06:12:21
	 * @param file 文件
	 * @return 内容字节数组
	 * @throws Exception 执行发生异常
	 */
	protected byte[] getNodeBodyFromFile(
			File file) throws Exception {
		if (file==null || !file.exists()) {
			return new byte[0];
		}
		//读取流
		return getNodeBodyFromStream(new FileInputStream(file));
	}
	
	
	/**
	 * 从读入流中获取内容
	 * @param is					读入流
	 * @return						内容字节数组
	 * @throws Exception		异常
	 * 2014-3-14
	 * @author 马宝刚
	 */
	protected byte[] getNodeBodyFromStream(InputStream is) throws Exception {
		//输出流
		ByteArrayOutputStream baos = null;
		//读取缓存
		byte[] bytes = new byte[DEFAULT_READ_BUFFER_SIZE];
		try {
            baos = new ByteArrayOutputStream();
            //实际读取字节数
            int readCount = is.read(bytes);
            while (readCount > 0) {
            	baos.write(bytes,0,readCount);
            	readCount = is.read(bytes); //继续读取
            }
            baos.flush(); //更新
            bytes = baos.toByteArray();
    		//判断内容是否被Base64加密，绕过了敏感字符检查 @b64@
    		if(bytes.length>5 
    				&& bytes[0]=='@' 
    				&& bytes[1]=='b'
    				&& bytes[2]=='6'
    				&& bytes[3]=='4'
    				&& bytes[4]=='@') {
    			bytes = Base64.decode(bytes,5,bytes.length-5);
    		}
    		md5Value = MD5.md5(bytes); //获取字节数组（文件）的MD5值（大写）
            return bytes;
        } catch (Exception e) {
        	e.printStackTrace();
        	throw new NodeHandlerException(e);
        }finally {
        	try {
        		is.close();
        	}catch(Exception e) {}
        }
	}
	
	
	/**
	 * 从html文件中获取html文本
	 * @author 刘虻
	 * @param filePath 文件路径
	 * @throws Exception 读取文件时发生异常
	 * 2006-10-22下午05:56:37
	 */
	protected byte[] getNodeBodyFromFile(
			String filePath) throws Exception {
		if (filePath==null 
				|| filePath.length()==0) {
			return new byte[0];
		}
		//输出流
		ByteArrayOutputStream baos = null;
		//读取缓存
		byte[] bytes = new byte[DEFAULT_READ_BUFFER_SIZE];
		//读取流
		InputStream fis = null;
		try {
            fis = NodeHandlerUtil.getFileStreamByPath(filePath);
            baos = new ByteArrayOutputStream();
            //实际读取字节数
            int readCount = fis.read(bytes);
            while (readCount > 0) {
            	baos.write(bytes,0,readCount);
            	readCount = fis.read(bytes); //继续读取
            }
            baos.flush(); //更新
            return baos.toByteArray();
        } catch (Exception e) {
        	e.printStackTrace();
        	throw new NodeHandlerException(e);
        }finally {
        	try {
        		fis.close();
        	}catch(Exception e) {}
        }
	}


	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2006-11-1上午12:35:03
	 */
	@Override
    public Object clone() throws CloneNotSupportedException {
		//构建新的类实例
		NodeHandler newNodeHandler = (NodeHandler)newInstance(true);
		newNodeHandler.dealEncode = dealEncode;
		newNodeHandler.filePath = filePath;
		super.nativeClone(newNodeHandler);
		return newNodeHandler;
	}

	
	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-23下午01:40:01
	 */
	@Override
    public IViewHandler clear() {
		super.clear();
		filePath = "";
		return this;
	}


	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-23下午01:38:17
	 */
	@Override
    public INodeHandler setNodeBody(String nodeBody) throws Exception {
		clear();
		outSelf = false; //根节点默认不显示
		if(nodeBody.startsWith("@b64@")) {
			nodeBody = new String(Base64.decode(nodeBody.getBytes(),5,nodeBody.length()-5));
		}
		if(nodeBody!=null && nodeBody.length()>0) {
			parse(nodeBody.getBytes(StandardCharsets.UTF_8),0);//解析html
			isEmpty = false; //设置非空节点
		}
		return this;
	}
	
	/**
	 * 将指定HTML内容设置到当前节点中 同：setNodeBody
	 * @param nodeBody html内容
	 * @return 当前类实例
	 * @throws Exception 异常
	 * 2017年11月8日
	 * @author MBG
	 */
	@Override
    public INodeHandler html(String nodeBody) throws Exception {
		return setNodeBody(nodeBody);
	}
	
	/**
	 * 将指定HTML内容设置到当前节点中 同：setNodeBody
	 * @param nodeBody html内容
	 * @return 当前类实例
	 * @throws Exception 异常
	 * 2017年11月8日
	 * @author MBG
	 */
	@Override
    public INodeHandler xml(String nodeBody) throws Exception {
		return setNodeBody(nodeBody);
	}
	
	/**
	 * 将HTML段作为子节点对象插入当前节点中
	 * @param nodeBody HTML内容
	 * @return 新增加的子节点对象
	 * @throws Exception 异常
	 * 2017年11月3日
	 * @author MBG
	 */
	@Override
    public INodeHandler addNodeBody(String nodeBody) throws Exception {
		//构建返回值
		INodeHandler node = (INodeHandler)newInstance(false);
		if(nodeBody!=null) {
			node.setNodeBody(nodeBody);
		}
		return node;
	}

	
	/**
	 * 将指定子页面作为对象插入到当前节点对象中
	 * @param filePath 子页面路径（绝对路径）
	 * @return 子页面对象
	 * @throws Exception 异常
	 * 2017年11月8日
	 * @author MBG
	 */
	@Override
    public INodeHandler addPath(String filePath) throws Exception {
		//构建返回值
		INodeHandler node = (INodeHandler)newInstance(false);
		if(filePath!=null && filePath.length()>0) {
			//子节点内容文件
			File cFile = new File(filePath);
			if(cFile.exists()) {
				node.setFile(cFile);
			}
		}
		return node;
	}
	
	/**
	 * 将指定子页面作为对象插入到当前节点对象中
	 * @param file 子页面文件对象
	 * @return 子页面对象
	 * @throws Exception 异常
	 * 2017年11月8日
	 * @author MBG
	 */
	@Override
    public INodeHandler addFile(File file) throws Exception {
		//构建返回值
		INodeHandler node = (INodeHandler)newInstance(false);
		//子节点内容文件
		if(file!=null && file.exists()) {
			node.setFile(file);
		}
		return node;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-23下午01:45:18
	 */
	@Override
    public INodeHandler writeFile(String filePath, String outEncoding) throws Exception {
		File file = null; //目标保存文件
		FileOutputStream fops = null;
		PrintStream pris = null;
		if(outEncoding==null) {
			outEncoding = getDealEncode();
		}
		try {
            //构造文件对象
            file = new File(filePath);
            if(!file.exists()) {
            	file.createNewFile();
            }
            //构造文件输出流
            fops = new FileOutputStream(file,true);
            //构造输出流
            pris = new PrintStream(fops,true,outEncoding);
            //执行输出
            pris.println(getNodeBody());
            //关闭流
            pris.close();
        }catch(Exception e) {
            e.printStackTrace();
            throw new NodeHandlerException("write file["+filePath+"] error"+e);
        }finally {
        	try {
        		fops.close();
        	}catch(Exception e) {
        		e.printStackTrace();
        	}
        	try {
        		pris.close();
        	}catch(Exception e) {
        		e.printStackTrace();
        	}
        }
        return this;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-23下午01:45:23
	 */
	@Override
    public INodeHandler writeFile() throws Exception {
		return writeFile(getFilePath(),null);
	}
	
	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2009-10-24上午11:49:38
	 */
	@Override
    public IViewHandler newInstance() {
		return newInstance(false);
	}
	
	
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-9-13 上午10:15:06
	 */
	@Override
    public IViewHandler n() {
		return newInstance(false);
	}
	
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-9-13 上午10:13:06
	 */
	@Override
    public IViewHandler newInstance(boolean alone) {
		//构建返回值
		NodeHandler reHh = new NodeHandler();
		reHh.locationInfo = locationInfo;
		reHh.dealEncode = dealEncode;
		reHh.filePath = filePath;
		reHh.xmlStyle = xmlStyle;
		if(!alone) {
			addChildNode(reHh);
		}
		return reHh;
	}
	
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-9-13 上午10:15:21
	 */
	@Override
    public IViewHandler n(boolean alone) {
		return newInstance(alone);
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-8-5 下午03:05:02
	 */
	@Override
    public long getTS() {
		return ts;
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-8-5 下午03:05:09
	 */
	@Override
    public void setTS(long ts) {
		this.ts = ts;
	}
	
	/**
	 * 如果解析的文件，可以返回这个文件的MD5值
	 * @return MD5值
	 * 2017年10月16日
	 * @author MBG
	 */
	@Override
    public String getMd5() {
		if(md5Value==null) {
			return "";
		}
		return md5Value;
	}
}
